ATOM Documentation

← Back to App

Accessibility Violation Reporting

**Purpose:** Standardized format for reporting WCAG violations with remediation guidance

**Version:** 1.0

**Last Updated:** 2026-03-22

---

Overview

This document defines the standardized format for reporting accessibility violations found during automated testing (Tier 1 & Tier 2) and manual testing (Tier 3). Each violation includes WCAG success criteria, severity level, affected component/page, issue description, remediation steps, and code examples.

**Why Standardized Reporting Matters:**

  • Consistent violation tracking across teams
  • Clear remediation guidance for developers
  • WCAG criteria reference for compliance auditing
  • Severity-based prioritization for fixing
  • Progress tracking over time

---

Violation Format

Each violation report MUST include:

Required Fields

  1. **WCAG Success Criteria**
  • Format: "WCAG 2.1 AA [X.X.X] - [Criteria Name]"
  • Example: "WCAG 2.1 AA 1.1.1 - Non-text Content"
  1. **Severity Level**
  • Values: critical, serious, moderate
  • See severity definitions below
  1. **Affected Component/Page**
  • Component name or page path
  • Example: "Login form (/login)" or "Button component"
  1. **Issue Description**
  • Clear explanation of the accessibility violation
  • Why it's a problem for users
  • When it occurs
  1. **Remediation Steps**
  • Specific code changes required
  • Before/after code examples
  • Testing steps to verify fix
  1. **Verification Checklist**
  • Automated test passes
  • Screen reader announces correctly
  • Keyboard navigation works

Optional Fields

  1. **Impact**
  • User impact description
  • Which assistive technologies affected
  • Frequency of occurrence
  1. **Screenshots**
  • Visual evidence (if applicable)
  • Screen reader output capture

---

Severity Levels

Critical

**Definition:** Blocks task completion, prevents user from using core functionality

**Examples:**

  • Unlabeled form submit button (cannot submit form)
  • Missing form labels (cannot input data)
  • No submit button at all (cannot complete action)
  • Keyboard trap (cannot exit component)
  • Missing alt text on informative image (cannot understand content)

**Remediation Timeline:** Fix immediately, block deployment

**User Impact:** Users cannot complete critical tasks

---

Serious

**Definition:** Major hindrance, significantly degrades experience but allows task completion with difficulty

**Examples:**

  • Missing alt text on decorative image (confusing but not blocking)
  • Low contrast text (difficult to read but possible)
  • Missing skip navigation link (tedious navigation)
  • Unclear link text ("click here")
  • Heading hierarchy broken (difficult navigation)

**Remediation Timeline:** Fix within sprint, prioritize above moderate

**User Impact:** Users can complete tasks but with significant difficulty

---

Moderate

**Definition:** Minor hindrance, slightly degrades experience but doesn't prevent task completion

**Examples:**

  • Skip link not visible on focus (hidden until focused)
  • Missing lang attribute (screen reader uses wrong language)
  • Redundant links (same link text to different destinations)
  • Missing aria-current on active navigation
  • Empty links or buttons

**Remediation Timeline:** Fix within quarter, backlog if needed

**User Impact:** Minor inconvenience, doesn't significantly hinder task completion

---

Reporting Template

Use this template when documenting accessibility violations:

## [Component/Page] - [WCAG Criteria]

**Severity:** Critical/Serious/Moderate
**WCAG:** 2.1 AA [X.X.X] - [Criteria Name]
**Impact:** [User impact description]
**Discovered:** [Automated test / Manual test - Date]

### Issue

[Clear description of the accessibility violation. Why is this a problem? Which users are affected? How does it prevent/hinder task completion?]

### How to Test

1. [Step 1: Navigate to...]
2. [Step 2: Activate screen reader...]
3. [Step 3: Observe that...]

**Expected:** [What should happen]
**Actual:** [What actually happens]

### Remediation

#### Before (Incorrect)

// [Code showing the accessibility violation]

<Button>&times;</Button>


#### After (Correct)

// [Fixed code with accessibility improvements]

<button aria-label="Close dialog" onClick={onClose}>

&times;

</button>


#### Changes Made

- [ ] Added aria-label to icon-only button
- [ ] Ensured keyboard navigation works
- [ ] Verified screen reader announcement

### Verification

- [ ] Automated test passes (jest-axe / axe-core)
- [ ] Screen reader announces correctly (NVDA + VoiceOver)
- [ ] Keyboard navigation works (Tab, Enter, Escape)
- [ ] Visual focus indicator visible

### Related Resources

- WCAG Criteria: [Link to criteria explanation]
- axe Rule: [Link to axe-core rule documentation]
- Similar Issue: [Link to related issue if applicable]

---

Priority Order for Remediation

**When multiple violations exist, fix in this order:**

1. Critical (Immediate)

  • Blocks task completion
  • Prevents users from using core functionality
  • Must fix before deployment

2. Serious (Within Sprint)

  • Major barriers to task completion
  • Significantly degrades experience
  • Users can complete tasks but with difficulty

3. Moderate (Within Quarter)

  • Minor hindrances
  • Slight degradation of experience
  • Doesn't prevent task completion

**Work Prioritization Formula:**

Priority = (Severity Weight) × (User Impact) × (Frequency)

Severity Weights:
- Critical: 10
- Serious: 5
- Moderate: 2

User Impact:
- All users: 3
- Most users: 2
- Some users: 1

Frequency:
- Always: 3
- Often: 2
- Rarely: 1

**Example Calculation:**

  • Critical (10) × All users (3) × Always (3) = 90 (HIGHEST PRIORITY)
  • Moderate (2) × Some users (1) × Rarely (1) = 2 (LOWEST PRIORITY)

---

Tracking Violations

GitHub Issues

Track accessibility violations in GitHub issues with labels:

**Required Labels:**

  • a11y - All accessibility issues
  • a11y-critical - Critical violations (blocks deployment)
  • a11y-serious - Serious violations (fix in sprint)
  • a11y-moderate - Moderate violations (fix in quarter)
  • a11y-automated - Found by automated tests
  • a11y-manual - Found by manual testing

**Issue Title Format:**

[A11y] [Component/Page]: [WCAG Criteria] - [Brief Description]

Examples:
[A11y] Login form: WCAG 1.3.1 - Missing form labels
[A11y] Button component: WCAG 2.4.7 - Focus not visible
[A11y] Pricing page: WCAG 1.4.3 - Low contrast text

**Issue Template:**

## Accessibility Violation

**Severity:** {{severity}}
**WCAG:** {{wcag_criteria}}
**Component/Page:** {{component_or_page}}
**Discovered:** {{date}} via {{automated_test | manual_test}}

### Issue

{{issue_description}}

### User Impact

{{user_impact_description}}

### How to Test

{{testing_steps}}

### Remediation

{{remediation_steps_with_code_examples}}

### Verification

- [ ] Automated test passes
- [ ] Screen reader announces correctly
- [ ] Keyboard navigation works

### Severity Priority

{{priority_justification}}

---

Common Violations and Fixes

1. Missing Alt Text (WCAG 1.1.1 - Non-text Content)

**Severity:** Serious (informative images), Critical (functional images)

**Issue:** Images missing alt text attribute

**Example Fix:**

// Before (Incorrect)
<img src="logo.png" />
<img src="warning-icon.png" />
<img src="decorative-pattern.png" />

// After (Correct)
<img src="logo.png" alt="Company Logo" />
<img src="warning-icon.png" alt="Warning: Action required" />
<img src="decorative-pattern.png" alt="" role="presentation" />

**Rules:**

  • Informative images: Describe content
  • Functional images: Describe function (e.g., "Search button")
  • Decorative images: Use alt="" and role="presentation"

**Testing:**

# Automated test
npx jest-axe

# Manual: Screen reader announces alt text or skips decorative images

---

2. Missing Form Labels (WCAG 1.3.1 - Info and Relationships)

**Severity:** Critical (blocks form submission)

**Issue:** Form inputs missing associated labels

**Example Fix:**

// Before (Incorrect)
<input type="email" placeholder="Email" />
<input type="password" placeholder="Password" />
<input type="checkbox" /> Remember me

// After (Correct)
<label for="email">Email</label>
<input id="email" type="email" aria-required="true" />

<label for="password">Password</label>
<input id="password" type="email" aria-required="true" />

<label>
  <input type="checkbox" name="remember" />
  Remember me
</label>

**Rules:**

  • Always use <label for="id"> with id="id" on input
  • Use aria-required="true" for required fields
  • For checkboxes/radios, wrap label around input

**Testing:**

# Automated test
npx jest-axe

# Manual: Screen reader announces label when landing on input

---

3. Low Color Contrast (WCAG 1.4.3 - Contrast (Minimum))

**Severity:** Serious (text), Moderate (UI components)

**Issue:** Text contrast fails 4.5:1 ratio for normal text, 3:1 for large text/UI components

**Example Fix:**

/* Before (Incorrect - Fails 4.5:1) */
.text {
  color: #999;  /* Light gray on white = 2.8:1 ratio */
  background: #fff;
}

.button {
  color: #fff;
  background: #3498db;  /* Blue on white = 2.7:1 ratio */
}

/* After (Correct - Passes 4.5:1) */
.text {
  color: #595959;  /* Dark gray on white = 7.0:1 ratio */
  background: #fff;
}

.button {
  color: #fff;
  background: #0066cc;  /* Darker blue on white = 5.7:1 ratio */
}

**Rules:**

  • Normal text (<18pt): 4.5:1 minimum contrast
  • Large text (≥18pt or ≥14pt bold): 3:1 minimum
  • UI components (icons, borders): 3:1 minimum

**Testing:**

# Automated test
npm run test:a11y:contrast

# Manual: Use axe DevTools or WebAIM Contrast Checker

---

4. Focus Not Visible (WCAG 2.4.7 - Focus Visible)

**Severity:** Serious (keyboard users)

**Issue:** No visible focus indicator on interactive elements

**Example Fix:**

/* Before (Incorrect) */
button:focus {
  outline: none;  /* Removes focus indicator */
}

/* After (Correct) */
button:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* Alternative: Custom focus indicator */
button:focus-visible {
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.5);
  background-color: #e6f2ff;
}

**Rules:**

  • Always provide visible focus indicator
  • Use :focus for mouse + keyboard, :focus-visible for keyboard only
  • Minimum 2px outline, offset from element
  • Color must meet 3:1 contrast against background

**Testing:**

# Manual: Tab through page, verify focus visible on all interactive elements

---

5. Missing ARIA Labels on Icon-Only Buttons (WCAG 2.5.3 - Label in Name)

**Severity:** Critical (icon-only buttons)

**Issue:** Buttons with only icons have no accessible name

**Example Fix:**

// Before (Incorrect)
<button onClick={onClose}>
  <XIcon />
</button>

<button onClick={onSearch}>
  <SearchIcon />
</button>

// After (Correct)
<button onClick={onClose} aria-label="Close dialog">
  <XIcon />
</button>

<button onClick={onSearch} aria-label="Search">
  <SearchIcon />
</button>

**Rules:**

  • Icon-only buttons MUST have aria-label
  • Label should describe button action, not icon name
  • Use "Close dialog", not "X icon"
  • Use "Search", not "Magnifying glass"

**Testing:**

# Automated test
npx jest-axe

# Manual: Screen reader announces "Close dialog button"

---

6. Modal Not Announced (WCAG 4.1.2 - Name, Role, Value)

**Severity:** Serious (modals)

**Issue:** Modal dialogs not announced to screen readers

**Example Fix:**

// Before (Incorrect)
<div className="modal">
  <h2>Delete Account</h2>
  <p>Are you sure?</p>
  <button onClick={onConfirm}>Yes</button>
  <button onClick={onCancel}>No</button>
</div>

// After (Correct)
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
  className="modal"
>
  <h2 id="modal-title">Delete Account</h2>
  <p>Are you sure?</p>
  <button onClick={onConfirm}>Yes, delete</button>
  <button onClick={onCancel}>No, cancel</button>
</div>

**Rules:**

  • Add role="dialog" to modal container
  • Add aria-modal="true" to trap focus
  • Add aria-labelledby pointing to modal title
  • Implement focus trap (JavaScript)
  • Return focus to trigger on close

**Testing:**

# Automated test
npm run test:a11y

# Manual: Screen reader announces "dialog appeared"

---

7. Broken Heading Hierarchy (WCAG 1.3.1 - Info and Relationships)

**Severity:** Moderate (navigation difficulty)

**Issue:** Heading levels skipped (h1 → h3), no h1 on page

**Example Fix:**

// Before (Incorrect - Skips h2)
<h1>Page Title</h1>
<h3>Section Title</h3>
<h4>Subsection</h4>

// After (Correct - No skipped levels)
<h1>Page Title</h1>
<h2>Section Title</h2>
<h3>Subsection</h3>

**Rules:**

  • Always start with h1 (one per page)
  • Don't skip heading levels (h1 → h2 → h3)
  • Don't use headings for styling (use CSS classes)
  • Nest headings correctly (h2 under h1, h3 under h2)

**Testing:**

# Automated test
npx jest-axe

# Manual: Screen reader heading navigation works correctly

---

8. Form Errors Not Announced (WCAG 3.3.1 - Error Identification)

**Severity:** Critical (form validation)

**Issue:** Form errors not announced to screen readers

**Example Fix:**

// Before (Incorrect)
{errors.email && <span className="error">{errors.email}</span>}
<input type="email" id="email" />

// After (Correct)
{errors.email && (
  <span role="alert" aria-live="polite" className="error">
    {errors.email}
  </span>
)}
<input
  type="email"
  id="email"
  aria-invalid={!!errors.email}
  aria-describedby={errors.email ? "email-error" : undefined}
/>
<span id="email-error" className="error">
  {errors.email}
</span>

**Rules:**

  • Add role="alert" to error messages (announced immediately)
  • Add aria-invalid="true" to invalid inputs
  • Add aria-describedby pointing to error message
  • Display errors inline, not only in alert()

**Testing:**

# Automated test
npx jest-axe

# Manual: Screen reader announces error after invalid submission

---

**Severity:** Moderate (navigation efficiency)

**Issue:** No way to skip navigation and jump to main content

**Example Fix:**

// Before (Incorrect - No skip link)
<header>...</header>
<main>...</main>

// After (Correct - Skip link added)
<a
  href="#main-content"
  className="skip-link"
>
  Skip to main content
</a>
<header>...</header>
<main id="main-content">...</main>

**CSS:**

.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  text-decoration: none;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}

**Rules:**

  • Add skip link as first focusable element on page
  • Link target should be id of <main> or first heading
  • Make skip link visible on focus
  • Text should be descriptive ("Skip to main content")

**Testing:**

# Manual: Tab from top of page, skip link appears and works

---

10. Duplicate IDs (WCAG 4.1.1 - Parsing)

**Severity:** Serious (breaks accessibility tree)

**Issue:** Multiple elements with same ID attribute

**Example Fix:**

// Before (Incorrect - Duplicate IDs)
{items.map((item) => (
  <div key={item.id}>
    <input id="name" name="name" />
    <label htmlFor="name">Name</label>
  </div>
))}

// After (Correct - Unique IDs)
{items.map((item, index) => (
  <div key={item.id}>
    <input id={`name-${index}`} name={`name-${index}`} />
    <label htmlFor={`name-${index}`}>Name</label>
  </div>
))}

**Rules:**

  • IDs MUST be unique within page
  • Use generated IDs for repeated components (useId hook, index)
  • Test for duplicates with axe-core

**Testing:**

# Automated test
npm run test:a11y

# Error: "The id 'name' is already used"

---

Remediation Workflow

1. Violation Discovered

**Automated Test (CI/CD):**

  • GitHub Actions workflow fails
  • Report generated and uploaded as artifact
  • Issue created automatically (if configured)

**Manual Test (Quarterly Audit):**

  • Tester documents violation in report
  • Issue created manually by QA team

2. Issue Triage

**Assign Severity:**

  • Use severity definitions above
  • Consider user impact and frequency
  • Add appropriate labels (a11y-critical, a11y-serious, a11y-moderate)

**Prioritize:**

  • Critical: Assign to current sprint, block deployment
  • Serious: Assign to current sprint, prioritize above moderate
  • Moderate: Add to backlog, schedule within quarter

3. Developer Implements Fix

**Follow remediation steps:**

  1. Read issue description and WCAG criteria
  2. Review code examples in issue
  3. Implement fix using provided template
  4. Run automated tests locally (npm run test:a11y)
  5. Test with screen reader (if critical/serious)

4. Verification

**Automated Tests:**

# Run full accessibility suite
npm run test:a11y:full

# Verify specific fix
npm run test:a11y:unit  # Component tests
npm run test:a11y       # E2E tests

**Manual Tests (for critical/serious):**

  • [ ] Test with NVDA (Windows + Firefox)
  • [ ] Test with VoiceOver (macOS + Safari)
  • [ ] Test keyboard navigation (Tab, Enter, Escape)
  • [ ] Verify fix addresses reported issue

5. Close Issue

**When issue is resolved:**

  • All automated tests passing
  • Manual verification complete (if required)
  • Fix deployed to production
  • Issue closed with reference to commit/PR

---

Progress Tracking

Accessibility Debt Metrics

Track these metrics monthly:

accessibility_debt:
  critical: 0  # Target: 0
  serious: 5   # Target: <5
  moderate: 23 # Target: <25

remediation_rate:
  new_violations_this_month: 8
  fixed_this_month: 12
  trend: "decreasing"  # Good!

compliance:
  wcag_2_1_aa_compliant: false
  estimated_compliance_date: "2026-06-01"

Quarterly Reports

Generate quarterly report showing:

  1. **Violations by Severity:**
  • Critical: [count] (target: 0)
  • Serious: [count] (target: <5)
  • Moderate: [count] (target: <25)
  1. **Violations by WCAG Criteria:**
  • Most common violations (top 10)
  • Target for developer training
  1. **Remediation Progress:**
  • Fixed this quarter: [count]
  • New this quarter: [count]
  • Net change: [+/-]
  1. **Compliance Status:**
  • % WCAG 2.1 AA compliant
  • Estimated full compliance date

---

Resources

**WCAG 2.1 AA Quick Reference:**

https://www.w3.org/WAI/WCAG21/quickref/

**axe-core Rule Documentation:**

https://www.deque.com/axe/core-docs/

**WebAIM Accessibility Checklist:**

https://webaim.org/standards/wcag/checklist

**WAI-ARIA Authoring Practices:**

https://www.w3.org/WAI/ARIA/apg/

**Accessibility Testing Tools:**

  • axe DevTools: https://www.deque.com/axe/devtools/
  • WAVE: https://wave.webaim.org/
  • Lighthouse: chrome://lighthouse

---

**Reporting Version:** 1.0

**Last Updated:** 2026-03-22

**Next Review:** 2026-06-22 (Quarterly)

**Owner:** QA Team + Development Team

**Related Documentation:**

  • docs/accessibility/THREE_TIER_STRATEGY.md (overall strategy)
  • docs/accessibility/SCREEN_READER_TESTING.md (procedures)
  • docs/accessibility/MANUAL_TESTING_CHECKLIST.md (checklist)